/* Copyright 2012 Oliver Katz

    This file is part of Theta.

    Theta is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "vm.h"

namespace vm
{
	void VM::generateMidComp()
	{
		for (map<string, Clause>::iterator i = clauses.begin(); i != clauses.end(); i++)
		{
			if (debugMidComp)
				cout << "\t+ " << i->first << "\n";
			midcomp.addSubDirectory(i->first);
			vector<string> p = i->second.getPlugins();
			for (vector<string>::iterator j = p.begin(); j != p.end(); j++)
			{
				midcomp.addFile(i->first, *j);
				stringstream ss;
				vector<string> c = i->second.getCfg(*j);
				for (vector<string>::iterator k = c.begin(); k != c.end(); k++)
				{
					ss << *k << " ";
				}
				string buf = ss.str();
				buf = buf.substr(0, buf.size()-1);
				if (debugMidComp)
					cout << "\t\t- " << *j << "(\"" << buf << "\")\n";
				midcomp.set(i->first, *j, buf);
			}
		}
	}

	void VM::reloadMidComp()
	{
		for (map<string, Clause>::iterator i = clauses.begin(); i != clauses.end(); i++)
		{
			vector<string> p = i->second.getPlugins();
			for (vector<string>::iterator j = p.begin(); j != p.end(); j++)
			{
				ParsingErrorBuffer tmp = ParsingErrorBuffer();
				cfg[i->first][*j] = tokenize(midcomp.get(i->first, *j), tmp);
			}
		}
	}

	int VM::storeAddress(string n, int a)
	{
		vartable[n] = a;
		return a;
	}

	int VM::getAddress(string n)
	{
		return vartable[n];
	}

	int VM::allocRegister()
	{
		for (map<int, bool>::iterator i = regtable.begin(); i != regtable.end(); i++)
		{
			if (i->second == false)
			{
				i->second = true;
				return i->first;
			}
		}

		return -1;
	}

	void VM::freeRegister(int r)
	{
		if (regtable.find(r) != regtable.end())
			regtable[r] = false;
	}

	BytecodeClause VM::generateFrame(Frame f)
	{
		BytecodeClause rtn = BytecodeClause();
		if (parser.isFunction(f.getFunction()))
		{
			for (vector<Frame>::iterator i = f.getBranches().begin(); i != f.getBranches().end(); i++)
			{
				BytecodeClause b = generateFrame(*i);
				rtn.append(b);
				rtn.write(_push, _rret);
			}
			rtn.write(_call, f.getFunction());
		}
		else if (parser.isOperator(f.getFunction()))
		{
			BytecodeClause a0 = generateFrame(f.getBranches()[0]);
			BytecodeClause a1 = generateFrame(f.getBranches()[1]);

			if (f.containsFloats())
			{
				if (f.getFunction().compare("+") == 0)
				{		
					rtn.write(_fadd, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("-") == 0)
				{
					rtn.write(_fsub, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("*") == 0)
				{
					rtn.write(_fmul, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("/") == 0)
				{
					rtn.write(_fdiv, a0.last(), a1.last());
				}
			}
			else
			{
				if (f.getFunction().compare("+") == 0)
				{
					rtn.write(_add, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("-") == 0)
				{
					rtn.write(_sub, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("*") == 0)
				{
					rtn.write(_mul, a0.last(), a1.last());
				}
				else if (f.getFunction().compare("/") == 0)
				{
					rtn.write(_div, a0.last(), a1.last());
				}
			}

			freeRegister(a1.last());
		}
		else if (parser.isSymbol(f.getFunction()) && f.getBranches().empty())
		{
			int r = allocRegister();
			rtn.setLast(r);
			rtn.write(_hldi, r, getAddress(f.getFunction()));
		}
		else if (parser.isValue(f.getFunction()))
		{
			if (f.getFunction()[0] == '\"')
			{
				char *buf = (char *)malloc(sizeof(char)*(f.getFunction().size()-1));
				strcpy(buf, f.getFunction().substr(1, f.getFunction().size()-2).c_str());
				t_data_meta dm = t_data_meta_init_with_type_value(H32, (long)buf);
				int a = t_data_meta_page_put_dirty(&data, dm);
				int r = allocRegister();
				rtn.setLast(r);
				rtn.write(_hldi, r, a);
			}
			else
			{
				t_data_meta dm;
				if (f.getFunction().find(".") == string::npos)
					dm = t_data_meta_init_with_type_value(H32, (long)atoi(f.getFunction().c_str()));
				else
				{
					
					dm = t_data_meta_init_with_type_value(S32, atof(f.getFunction().c_str()));
				}

			}
		}

		return rtn;
	}

	VM::VM()
	{
		stringstream ss;
		ss << "theta" << getpid();
		name = ss.str();
		data = t_data_meta_page_init();
		midcomp = DynamicDirectory(name);
		parser = Parser();
	}

	int VM::emulate(string path)
	{
		if (showProgress)
			cout << "parsing " << path << "...\n";
		clauses = parser.parse(parser.readFile(path));

		if (showProgress)
			cout << "generating mid-compilation dynamic filesystem...\n";
		generateMidComp();

		if (showProgress)
			cout << "waiting for mid-compilation dynamic filesystem unlock... ";
		while(midcomp.checkLock() == true)
		{
			sleep(1);
		}
		if (showProgress)
			cout << "unlocked.\n";

		reloadMidComp();

		if (showProgress)
			cout << "compiling bytecode...\n";

		return 0;
	}
}